feat(browser): Start a navigation span for bfcache restores - #23748
feat(browser): Start a navigation span for bfcache restores#23748logaretm wants to merge 1 commit into
Conversation
c306449 to
831a206
Compare
size-limit report 📦
|
831a206 to
d7cd440
Compare
d7cd440 to
e67604c
Compare
e67604c to
08b4500
Compare
08b4500 to
62457db
Compare
62457db to
7e4c2bc
Compare
48fcffe to
b7439e2
Compare
b7439e2 to
14b181f
Compare
14b181f to
18c743a
Compare
18c743a to
5c7be80
Compare
5c7be80 to
18ab1ef
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 18ab1ef. Configure here.
18ab1ef to
268dcd7
Compare
268dcd7 to
4568daa
Compare
| // Deliberately no `startTime`: the span starts now, at the restore. The | ||
| // `PerformanceNavigationTiming` entry still describes the original document load and | ||
| // would date the span to before the page was frozen. | ||
| name: hasSpanStreamingEnabled(client) | ||
| ? NAVIGATION_SPAN_NAME_FALLBACK | ||
| : WINDOW.location?.pathname || '/', | ||
| attributes: { | ||
| [SENTRY_SEGMENT_NAME_SOURCE]: 'url', | ||
| [SENTRY_ORIGIN]: 'auto.navigation.browser.bfcache', | ||
| // A bfcache restore is near-instant, so these spans would otherwise drag | ||
| // navigation duration percentiles down with no way to tell them apart. |
There was a problem hiding this comment.
Bug: Multiple Sentry client initializations lead to duplicate pageshow event listeners on the global WINDOW object, causing redundant navigation spans to be created.
Severity: MEDIUM
Suggested Fix
To prevent duplicate listeners, add a guard within the afterAllSetup function to check if the pageshow listener has already been attached to the WINDOW object before adding it. This will ensure that only one listener is active, regardless of how many clients are initialized.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: packages/browser/src/tracing/browserTracingIntegration.ts#L698-L720
Potential issue: Initializing multiple Sentry clients, which can occur in environments
like microfrontends or with hot reloading, causes the `afterAllSetup` hook to run for
each client instance. This hook adds a `pageshow` event listener to the global `WINDOW`
object. Because there is no check to see if a listener has already been attached,
multiple identical listeners accumulate. When a `pageshow` event with `persisted=true`
is fired by the browser, all of these accumulated listeners will execute, resulting in
the creation of duplicate navigation spans for a single user navigation.
Did we get this right? 👍 / 👎 to inform future reviews.
… bfcache Prototype. A bfcache restore resurrects the frozen document, so there is no document load and no usable history event: `popstate` either doesn't fire or is swallowed, because the URL is unchanged from when the page was frozen. Two independent guards in the existing path suppress it, neither written with bfcache in mind, so there is no small nudge that gets a span out of it. Without one, everything after the restore joins the trace the page had before it was frozen, separated by however long it sat in the cache. That misattributes errors, breadcrumbs, clicks and fetches, not just the web vitals that prompted this. The span is started from a `pageshow` listener in `browserTracingIntegration` rather than `bfcacheIntegration`, so it does not depend on an opt-in integration that is about hit/miss diagnostics. It is gated on `instrumentNavigation` and on by default. It carries `browser.navigation.type: bfcache`. A restore is near-instant, so without a way to filter these out they would drag navigation duration percentiles down exactly the way bfcache vitals would have dragged LCP. The span deliberately starts at the `pageshow` event rather than from `PerformanceNavigationTiming`, which is not replaced on restore and still describes the original document load. Known gap, pinned by a test: `bfcacheIntegration` registers its own `pageshow` listener from `setupOnce`, which core always runs before every `afterAllSetup`, so its hit/miss metric is emitted before this span exists and still lands on the pre-freeze trace.
4568daa to
83d3b23
Compare

Starts a navigation span when the page is restored from the back/forward cache.
A restore resurrects the frozen document, so there is no document load and no usable history event:
popstateeither doesn't fire or is swallowed because the URL is unchanged. Without a span of its own, everything after the restore joins the trace the page had before it was frozen, which misattributes errors and breadcrumbs as much as vitals.It lives in
browserTracingIntegrationrather thanbfcacheIntegrationso it doesn't depend on an opt-in integration being enabled, and carriesbrowser.navigation.type: bfcachesince a restore is near-instant and would otherwise drag navigation duration percentiles down with no way to tell it apart.Stacked on #23425.